package com.mofeng.myshop.until;

import org.openjdk.jol.datamodel.DataModel;
import org.openjdk.jol.info.ClassData;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import org.openjdk.jol.vm.VirtualMachine;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * <dependency>
 * <groupId>org.openjdk.jol</groupId>
 * <artifactId>jol-core</artifactId>
 * <version>0.17</version>
 * </dependency>
 */
public class MyClassLayout {

    private ClassLayout classLayout;
    private Object instance;

    static final String[] ZERO_RUNS;

    static {
        ZERO_RUNS = new String[16];
        String s = "";
        for (int c = 0; c < ZERO_RUNS.length; c++) {
            ZERO_RUNS[c] = s;
            s += "0";
        }
    }

    private MyClassLayout(Object instance) {
        this.instance = instance;
        this.classLayout = ClassLayout.parseInstance(instance);
    }

    public String toPrintableSimple(boolean withoutHex) {
        String markStr = "";
        String classStr = "";
        String arrLenStr = "";
        try {
            Class<? extends ClassLayout> classLayoutClass = classLayout.getClass();
            Field modelField = classLayoutClass.getDeclaredField("model");
            modelField.setAccessible(true);
            DataModel model = (DataModel) modelField.get(classLayout);

            Field classDataField = classLayoutClass.getDeclaredField("classData");
            classDataField.setAccessible(true);
            ClassData classData = (ClassData) classDataField.get(classLayout);

            int markSize = model.markHeaderSize();
            int classSize = model.classHeaderSize();
//            int arrSize = model.arrayLengthHeaderSize();

            int markOffset = 0;
            int classOffset = markOffset + markSize;
            int arrOffset = classOffset + classSize;


            if (classLayout != null) {
                VirtualMachine vm = VM.current();
                if (markSize == 8) {
                    long mark = vm.getLong(instance, markOffset);
                    Method parseMarkWordLong = classLayoutClass.getDeclaredMethod("parseMarkWord", long.class);
                    parseMarkWordLong.setAccessible(true);
                    String decoded = (classSize > 0) ? parseMarkWordLong.invoke(classLayout, mark).toString() : "(Lilliput)";
                    markStr = (withoutHex ? toHex(mark) : formatBinary(Long.toBinaryString(mark), 64)) + " " + decoded;
                } else if (markSize == 4) {
                    int mark = vm.getInt(instance, markOffset);
                    Method parseMarkWordInt = classLayoutClass.getDeclaredMethod("parseMarkWord", int.class);
                    parseMarkWordInt.setAccessible(true);
                    String decoded = (classSize > 0) ? parseMarkWordInt.invoke(classLayout, mark).toString() : "(Lilliput)";
                    markStr = (withoutHex ? toHex(mark) : formatBinary(Integer.toBinaryString(mark), 64)) + " " + decoded;
                }

                if (classSize == 8) {
                    long aLong = vm.getLong(instance, classOffset);
                    classStr = (withoutHex ? toHex(aLong) : formatBinary(Long.toBinaryString(aLong), 64));
                } else if (classSize == 4) {
                    int anInt = vm.getInt(instance, classOffset);
                    classStr = (withoutHex ? toHex(anInt) : formatBinary(Long.toBinaryString(anInt), 64));
                }

                if (classData.isArray()) {
                    arrLenStr = Integer.toString(vm.getInt(instance, arrOffset));
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
//        catch (IllegalAccessException e) {
//            e.printStackTrace();
//        } catch (NoSuchMethodException e) {
//            e.printStackTrace();
//        } catch (InvocationTargetException e) {
//            e.printStackTrace();
//        }
        return markStr;
    }

    private static String toHex(int x) {
        String s = Integer.toHexString(x);
        int deficit = 8 - s.length();
        return "0x" + ZERO_RUNS[deficit] + s;
    }

    private static String toHex(long x) {
        String s = Long.toHexString(x);
        int deficit = 16 - s.length();
        return "0x" + ZERO_RUNS[deficit] + s;
    }

    /**
     * 将二进制字符串格式化为指定长度且每4位空格的格式
     *
     * @param binaryStr 原始二进制字符串
     * @param width     指定的输出长度（位数）
     * @return 格式化后的二进制字符串（固定长度，每4位一个空格）
     */
    public static String formatBinary(String binaryStr, int width) {
        // 1. 使用String.format()将二进制字符串补全到指定长度
        //    "%" + width + "s" 创建格式字符串（如"%16s"）
        //    replace(' ', '0') 将补全的空格替换为零
        String binaryStrFixed = String.format("%" + width + "s", binaryStr).replace(' ', '0');

        // 2. 对固定长度的二进制字符串进行每4位加空格处理
        return formatBinary(binaryStr, width, 8);
    }

    /**
     * 将二进制字符串格式化为指定长度且每4位空格的格式
     *
     * @param binaryStr 原始二进制字符串
     * @param width     指定的输出长度（位数）
     * @param fixed     指定的分隔长度（位数）
     * @return 格式化后的二进制字符串（固定长度，每4位一个空格）
     */
    public static String formatBinary(String binaryStr, int width, int fixed) {
        // 1. 使用String.format()将二进制字符串补全到指定长度
        //    "%" + width + "s" 创建格式字符串（如"%16s"）
        //    replace(' ', '0') 将补全的空格替换为零
        String binaryStrFixed = String.format("%" + width + "s", binaryStr).replace(' ', '0');

        // 2. 对固定长度的二进制字符串进行每4位加空格处理
        return binaryStrFixed.replaceAll("(.{" + fixed + "})", "$1 ").trim();
    }

    public String toPrintable() {
        return classLayout.toPrintable();
    }

    public static MyClassLayout parseInstance(Object object) {
        return new MyClassLayout(object);
    }
}
